package com.caocaog.market.pay;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.caocaog.market.entity.*;
import com.caocaog.market.merchant.entity.MkMerchantBasic;
import com.caocaog.market.merchant.service.MkMerchantService;
import com.caocaog.market.pay.syb.dto.SybDto;
import com.caocaog.market.pay.syb.utils.SybPay;
import com.caocaog.market.vo.MemberPointModifyVO;
import org.jeecg.common.bdPay.BDPayUtil;
import com.caocaog.market.pay.vo.PayOrderVo;
import com.caocaog.market.pay.vo.RefundOrderVo;
import com.caocaog.market.pay.wxpay.BDPay;
import com.caocaog.market.pay.wxpay.WxPay;
import com.caocaog.market.service.*;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.WxPayKit;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Description: UnionPay
 * @author: Yuki Guo
 * @date: 2021年11月01日 11:14
 */

@Slf4j
@Component
@EnableScheduling
public class UnionPay {

    @Autowired
    private WxPay wxPay;
    @Autowired
    private BDPay bdPay;
    @Autowired
    private BDPayUtil bdPayUtil;
    @Autowired
    private IOrderRefundService orderRefundService;
    @Autowired
    private IOrderPayService orderPayService;
    @Autowired
    private IOrderBaseService orderBaseService;
    @Autowired
    private IOrderLineService orderLineService;
    /*@Autowired
    private IGoodsHisService goodsHisService;*/
    @Autowired
    private GoodsSpecService goodsSpecificationsService;
    @Autowired
    private IOrderStatusInfoService orderStatusInfoService;
    @Resource
    private IMemberPointStatementService memberPointStatementService;
    @Resource
    private MkMerchantService merchantService;


    /**
     * 统一支付
     *
     * @param orderId      订单ID
     * @param //payType    支付类型 1微信 2支付宝
     * @param //wechatType 微信支付类型 1app 2小程序
     * @param //price      价格
     * @param nofity       回调地址          填写notify/{需要填写} 里面的
     * @param body         支付标题
     * @param attach       支付额外参数
     * @param openId       小程序openid
     * @return
     */

    @Resource
    private SybPay sybPay;


    /**
     * 通联的支付方法
     *
     * @param sybDto 支付的DTO
     * @return 支付结果
     */
    @Transactional
    public Result<?> payUnion(SybDto sybDto) {

        sybDto.setBody("曹操来了-发单支付");
        try {
            OrderPay orderPay = orderPayService.getOne(new QueryWrapper<OrderPay>().eq("order_id", sybDto.getOrderId()).orderByDesc("create_time").last(" limit 1 "));
            Map<String, String> pay = sybPay.pay(sybDto, "/sybNotify");

            orderPay.setPaySerno(pay.get("trxid"));
            orderPay.setPayTime(new Date());
            orderPayService.updateById(orderPay);

            return Result.OK(pay);
        } catch (Exception e) {
            log.error("调起通联支付失败:", e);
            return Result.error("支付失败");
        }
    }

    /**
     * 微信的支付方法
     *
     * @param orderId 订单ID
     * @param notify  回调接口
     * @param body    消息体
     * @param attach  额外信息
     * @param openId  微信ID
     * @return 支付结果
     */
    @Transactional
    public Result<?> payWx(String orderId, String notify, String body,
                           String attach, String openId) {

        OrderBase orderBase = orderBaseService.getById(orderId);
        OrderPay orderPay = orderPayService.getOne(new QueryWrapper<OrderPay>().eq("order_id", orderId).orderByDesc("create_time").last(" limit 1 "));
        Result<?> result = null;
        MkMerchantBasic merchantBasic = merchantService.getById(orderBase.getMerchantId());

        //支付参数
        PayOrderVo vo = new PayOrderVo();

        vo.setBody(body);
        vo.setAttach(attach);
        vo.setOrderId(orderId);
        vo.setTotalPrice(new BigDecimal(orderPay.getPayAmount()));
        // 调试使用
//        vo.setTotalPrice(new BigDecimal("0.01"));
        vo.setNotifyUrl(notify + "/wx");
        vo.setPayType("1");
        vo.setOutTradeNo("P" + orderBase.getOrderNo());//todo 订单支付记录id
        vo.setWechatType(2);
        vo.setOpenId(openId);
        vo.setAppid(merchantBasic.getAppid());
        vo.setCusid(merchantBasic.getCusid());
        // 在构建出微信订单的同时去更新订单Pay表
        orderPay.setPaySerno(vo.getOutTradeNo());
        orderPayService.updateById(orderPay);
        result = wxPay.appPay(vo);
        return result;
    }

    /**
     * 字节小程序支付
     */
    @Transactional
    public JSONObject ttPay(String orderId, String body, String attach, String openId) {
        OrderBase orderBase = orderBaseService.getById(orderId);
        OrderPay orderPay = orderPayService.getOne(new QueryWrapper<OrderPay>().eq("order_id", orderId).orderByDesc("create_time").last(" limit 1 "));
        //支付参数
        PayOrderVo vo = new PayOrderVo();

        vo.setBody(body);
        vo.setAttach(attach);
        vo.setTotalPrice(new BigDecimal(orderPay.getPayAmount()));
        vo.setPayType("1");
        vo.setOutTradeNo("P" + orderBase.getOrderNo());
        vo.setWechatType(2);
        vo.setOpenId(openId);
        //update orderPay
        orderPay.setPaySerno(vo.getOutTradeNo());
        orderPay.setPayWay(4);
        orderPayService.updateById(orderPay);
        return bdPay.ttAppletPay(vo);
    }

//微信订单退款

    /**
     * 申请退款
     * Parameters:
     * orderId - orderId
     * orderNo - orderNo
     * refundNo - refundNo
     * refundAmount - refundAmount
     * Returns:
     * String 请求返回的结果
     *
     * @return
     */
    public Result<?> refund(String refundId, String outTradeNo, Integer payWay) {
        OrderRefund orderRefund = orderRefundService.getById(refundId);

        RefundOrderVo vo = new RefundOrderVo();
        vo.setCause(orderRefund.getCause());
        vo.setOrderId(orderRefund.getOrderId());
        vo.setOrderNo(orderRefund.getOrderNo());
        vo.setRefundAmount(orderRefund.getRefundAmount());
        vo.setOutRefundNo("P" + orderRefund.getRefundNo());
        vo.setOutTradeNo(outTradeNo);
        vo.setNotifyUrl("/wxRefund");
        //update orderRefund
        orderRefund.setPayRefundSerno(vo.getOutRefundNo());
        orderRefundService.updateById(orderRefund);

//        if (payWay == 4 || payWay == 5) {
//            return Result.OK(bdPay.ttRefund(vo));
//        } else {
        return wxPay.refund(vo);
//        }
    }

    /**
     * 微信回调统一处理
     */
    @Transactional
    public Result<?> wxNotify() {
        Lock lock = new ReentrantLock();
        try {
            lock.lock();
            HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder
                    .getRequestAttributes())).getRequest();
            String xmlMsg = HttpKit.readData(request);
            log.info("------xmlMsg: " + xmlMsg);
            Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
            String returnCode = params.get("return_code");
            log.info("------returnCode: " + returnCode);
            // 注意重复通知的情况，同一订单号可能收到多次通知，请注意一定先判断订单状态
            // 注意此处签名方式需与统一下单的签名类型一致
            if (WxPayKit.verifyNotify(params, wxPay.getApiConfig().getPartnerKey(), SignType.HMACSHA256)) {
                if (WxPayKit.codeIsOk(returnCode)) {
                    // 更新订单信息
                    String attach = params.get("attach");
                    //订单编号
                    String out_trade_no = params.get("out_trade_no");
                    log.info("------attach: " + attach + " ------out_trade_no: " + out_trade_no);
                    OrderPay orderPay = orderPayService.getOne(new QueryWrapper<OrderPay>().eq("pay_serno", out_trade_no));

                    if (null != orderPay && orderPay.getPayStatus() == 0) {
                        // 填入支付成功信息
                        //更新订单信息
                        orderPay.setEndPayInfo(params.toString());
                        orderPay.setPayStatus(1);//支付成功
                        orderPayService.updateById(orderPay);
                        OrderBase orderBase = orderBaseService.getById(orderPay.getOrderId());
                        orderBase.setStatus("1");
                        orderBaseService.updateById(orderBase);
                        // 支付成功的同时去变动一下积分信息
                        paySuccessCallbackOfMemberPointModify(orderBase);
//                    orderPayService.savePayEndInfo(out_trade_no,params.toString());
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("payNo", out_trade_no);
                        jsonObject.put("attach", attach);
                        log.info("------jsonObject: " + jsonObject);
                        return Result.OK(jsonObject);
                    }
                }
            }
            return Result.error("-----error:" + params.toString());
        } finally {
            lock.unlock();
        }
    }

    /**
     * 通联回调
     */
    @Transactional
    public Result<?> sybNotify(TreeMap<String, String> params) {
        //订单编号
        String out_trade_no = params.get("trxid");
        OrderPay orderPay = orderPayService.getOne(new QueryWrapper<OrderPay>().eq("pay_serno", out_trade_no));

        if (null != orderPay && orderPay.getPayStatus() == 0) {
            // 填入支付成功信息
            //更新订单信息
            orderPay.setEndPayInfo(params.toString());
            orderPay.setPayStatus(1);//支付成功
            orderPayService.updateById(orderPay);
            OrderBase orderBase = orderBaseService.getById(orderPay.getOrderId());
            orderBase.setStatus("1");
            orderBaseService.updateById(orderBase);


            return Result.OK();
        }
        log.warn("订单不是待支付状态");
        return Result.error("-----error:" + params.toString());
    }

    /**
     * 字节小程序支付回调
     */
    @Transactional
    public JSONObject bdNotify(JSONObject object) {
        JSONObject result = bdPay.ttAppletNotify(object);
        if (result.getInteger("err_no") != 0) {
            return result;
        }

        JSONObject msg = object.getJSONObject("msg");
        // 开发者侧的订单号 (P + orderNo)
        String cpOrderNo = msg.getString("cp_orderno");
        // 支付渠道：1-微信支付，2-支付宝支付，10-抖音支付（没用抖音支付，所以way只会是1或2）
        String way = msg.getString("way");

        OrderPay orderPay = orderPayService.getOne(new QueryWrapper<OrderPay>().eq("pay_serno", cpOrderNo));

        if (null != orderPay && orderPay.getPayStatus() == 0) {
            // 填入支付成功信息
            //更新订单信息
            orderPay.setEndPayInfo(object.toString());
            orderPay.setPayStatus(1); // 支付成功
            orderPay.setPayWay(way.equals("1") ? 4 : 5); // 支付方式
            orderPayService.updateById(orderPay);
            OrderBase orderBase = orderBaseService.getById(orderPay.getOrderId());
            orderBase.setStatus("1");
            orderBaseService.updateById(orderBase);
        }
        return result;
    }

    /**
     * 字节小程序退款回调
     */
    @Transactional
    public JSONObject bdRefundNotify(JSONObject object) {
        return bdPay.ttAppletNotify(object);
    }

    /**
     * 微信回调统一处理
     */
    @Transactional
    public Result<?> wxRefundNotify() {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder
                .getRequestAttributes())).getRequest();
        String xmlMsg = HttpKit.readData(request);
        log.info("------xmlMsg: " + xmlMsg);
        Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
        String returnCode = params.get("return_code");
        log.info("------returnCode: " + returnCode);

        return Result.OK();
    }


    //取消订单 每15分钟
    @Scheduled(cron = "0 0/15 * * * ?")
    public void cancelOrder() {

        String now = DateUtils.now();
        int num = 0;
        List<OrderBase> orderBaseList = orderBaseService.list(new QueryWrapper<OrderBase>().eq("status", 0));
        log.info("orderBaseList.size() > " + orderBaseList.size());
        for (OrderBase orderBase : orderBaseList) {
            OrderPay orderPay = orderPayService.getOne(new QueryWrapper<OrderPay>().eq("order_id", orderBase.getId()));
            log.info("orderPay = ");
            log.info(orderPay.toString());
            if (oConvertUtils.isNotEmpty(orderPay) && orderPay.getPayStatus() == 0) {
                Calendar calSrc = Calendar.getInstance();
                calSrc.setTime(orderPay.getCreateTime());
                Calendar calDes = Calendar.getInstance();
                calDes.setTime(new Date());
                log.info("calDes> {}, calSrc> {}, DateUtils.dateDiff> {}", calDes, calSrc, DateUtils.dateDiff('m', calDes, calSrc));
                if (DateUtils.dateDiff('m', calDes, calSrc) >= 15) {
                    List<OrderLine> orderLineList = orderLineService.list(new QueryWrapper<OrderLine>().eq("order_id", orderBase.getId()));
                    for (OrderLine line : orderLineList) {
//                        GoodsHis goodsHis = goodsHisService.getById(line.getGoodsHisId());
//                        GoodsBasic goodsBasic = goodsBasicService.getById(goodsHis.getGoodsId());
                        if (!line.getSpecHisId().isEmpty()) {
                            GoodsSpec goodsSpec = goodsSpecificationsService.getGoodsSpec(line.getSpecHisId());
                            if (goodsSpec != null) {
                                goodsSpec.setStock(goodsSpec.getStock() + line.getQuantity());//回滚库存
                                goodsSpecificationsService.updateGoodsSpec(goodsSpec);
                            }
                        }
                        // TODO 队列 回滚销量
                        //销量放弃回滚 销量数字在客户端展示看着好看:)
                        //  error Unexpected error occurred in scheduled task java.lang.NullPointerException: null
                        /*Integer saleNum = goodsBasic.getSaleNum() - line.getQuantity();
                        goodsBasic.setSaleNum(saleNum < 0 ? 0 : saleNum);//回滚销量
                        goodsBasicService.updateById(goodsBasic);*/
                    }
                    orderPay.setPayStatus(2);//支付失败 订单取消
                    orderPayService.updateById(orderPay);
                    orderBase.setStatus("-1");//订单取消
                    orderBaseService.updateById(orderBase);
                    num += 1;
                    //添加状态记录
                    OrderStatusInfo orderStatusInfo = OrderStatusInfo.builder().orderId(orderBase.getId())
                            .orderNo(orderBase.getOrderNo()).orderStatusName("订单已取消").createBy("系统自动取消")
                            .createTime(new Date()).remark("").build();
                    orderStatusInfoService.save(orderStatusInfo);
                    log.info("----------------取消订单-" + orderBase.getOrderNo() + "-成功------------------");
                }
            }
        }
        log.info("----------------取消订单个数-" + num + " :结束------------------");
    }


    /**
     * 支付成功后的积分变动方法
     *
     * @param orderBase 订单基础信息
     */
    public void paySuccessCallbackOfMemberPointModify(OrderBase orderBase) {
        BigDecimal takePoint = orderBase.getTakePoint();
        if (takePoint == null || BigDecimal.ZERO.compareTo(takePoint) == 0) {
            log.info("订单 {} ，获得积分为0，积分无变动！", orderBase.getId());
            return;
        }
        MemberPointModifyVO memberPointModifyVO = new MemberPointModifyVO();
        memberPointModifyVO.setMemberId(orderBase.getMemberId()).setPoint(orderBase.getTakePoint()).setModifyType(CacheConstant.POINT_MODIFY_TYPE_EXPEND).setModifyMessage(String.format("购买店内商品获得积分，消费订单信息为 id:{%s}。", orderBase.getId()));
        memberPointStatementService.modifyMemberPoint(memberPointModifyVO);
    }

    /**
     * 退款成功后的积分变动方法
     *
     * @param orderBase 订单基础信息
     */
    public void refundSuccessCallbackOfMemberPointModify(OrderBase orderBase) {
        BigDecimal takePoint = orderBase.getTakePoint();
        if (takePoint == null || BigDecimal.ZERO.compareTo(takePoint) == 0) {
            log.info("订单 {} ，获得积分为0，积分无变动！", orderBase.getId());
            return;
        }
        MemberPointModifyVO memberPointModifyVO = new MemberPointModifyVO();
        memberPointModifyVO.setMemberId(orderBase.getMemberId()).setPoint(orderBase.getTakePoint().negate()).setModifyType(CacheConstant.POINT_MODIFY_TYPE_REFUND).setModifyMessage(String.format("订单退款退还积分，消费订单信息为 id:{%s}。", orderBase.getId()));
        memberPointStatementService.modifyMemberPoint(memberPointModifyVO);
    }
}

